#include "broker.h"

#define MAX_SEM_NAME_SIZE 16

const int BCSHM_DATA_SIZES[] = {
    sizeof(EVENT_t)
  , sizeof(ABORT_t)
  , sizeof(FCACTION_t)
  , sizeof(flight_task_t)
  , sizeof(ftc_tlm_t)
  , sizeof(fc_tlm_t)
  , sizeof(bc_tlm_t)
};

void bcshmseg_init(struct bcshmseg_t *shmseg){

  shmseg->event = EVENT_UNKNOWN;
  shmseg->abort = ABORT_FALSE;
  shmseg->fc_action = FCACTION_EMPTY;

  flight_task_init(&shmseg->flight_task);
  ftc_tlm_init(&shmseg->ftc_tlm);
  fc_tlm_init(&shmseg->fc_tlm);
  bc_tlm_init(&shmseg->bc_tlm);

}

void bcshm_create(){
  int shm_fd;
  sem_t* sem_ptr[SHM_MAX];

  assert((shm_fd = shm_open(BCSHM_NAME, O_CREAT | O_RDWR, 0777)) != -1);

  for(int i = 0; i < SHM_MAX; ++i){
    char sem_name[MAX_SEM_NAME_SIZE];
    sprintf(sem_name, "%s%d", BCSEM_NAME, i);
    sem_ptr[i] = sem_open(sem_name, O_CREAT, 0777, 0);

    assert(sem_ptr[i] != SEM_FAILED);
  }
  
  assert(ftruncate(shm_fd, sizeof(struct bcshmseg_t)) != -1);
  
  for(int i = 0; i < SHM_MAX; ++i){
    assert(sem_close(sem_ptr[i]) != -1);
  }

  assert(close(shm_fd) != -1);
}

void bcshm_open(struct bcshm_t* shm){
  assert((shm->shm_fd = shm_open(BCSHM_NAME, O_RDWR, 0777)) != -1);

  assert((shm->shm_ptr = mmap(NULL, sizeof(struct bcshmseg_t), PROT_READ | PROT_WRITE, MAP_SHARED, shm->shm_fd, 0)) != MAP_FAILED);

  for(int i = 0; i < SHM_MAX; ++i){
    char sem_name[16];
    sprintf(sem_name, "%s%d", BCSEM_NAME, i);

    assert((shm->sem_ptr[i] = sem_open(sem_name, 0)) != SEM_FAILED);
  }
}

void bcshm_close(struct bcshm_t* shm){
  assert(munmap(shm->shm_ptr, sizeof(struct bcshmseg_t)) != -1);
  assert(close(shm->shm_fd) != -1);
}

void bcshm_delete(){
  if(shm_unlink(BCSHM_NAME) == -1)
    return;
  
  for(int i = 0; i < SHM_MAX; ++i){
    char sem_name[16];
    sprintf(sem_name, "%s%d", BCSEM_NAME, i);
    assert(sem_unlink(sem_name) != -1);
  }
}

void bcshm_init(){
  struct bcshm_t shm;
  
  bcshm_create();
  bcshm_open(&shm);

  struct bcshmseg_t shmseg;
  bcshmseg_init(&shmseg);
  memcpy(shm.shm_ptr, &shmseg, sizeof(shmseg));
  
  for(int i = 0; i < SHM_MAX; ++i)
    assert(sem_post(shm.sem_ptr[i]) != -1);

  bcshm_close(&shm);
}

void bcshm_read(enum bcshm_data_t idx, struct bcshm_t* shm, void* seg){
  assert(sem_wait(shm->sem_ptr[idx]) != -1); 
  memcpy(seg, shm->shm_ptr + bcshm_offset(idx), BCSHM_DATA_SIZES[idx]);
  assert(sem_post(shm->sem_ptr[idx]) != -1);
}

void bcshm_write(enum bcshm_data_t idx, struct bcshm_t* shm, void* seg){
  assert(sem_wait(shm->sem_ptr[idx]) != -1); 
  memcpy(shm->shm_ptr + bcshm_offset(idx), seg, BCSHM_DATA_SIZES[idx]);
  assert(sem_post(shm->sem_ptr[idx]) != -1);
}

void bcshmseg_print(struct bcshmseg_t* shmseg){
  printf("Flight task:\n");
  printf("\tHight: %u\n", shmseg->flight_task.hight);
  printf("\tStart time: %d\n", shmseg->flight_task.start_time);
  printf("\tHang time: %d\n", shmseg->flight_task.hang_time);
};

int bcshm_offset(enum bcshm_data_t idx){
  int res = 0;
  for(int i = 0; i < idx; ++i){
    res += BCSHM_DATA_SIZES[i];
  }
  return res;
}
